import tkinter as tk
from tkinter import simpledialog, messagebox
import subprocess
import time
import threading
import socket
import os
import sys
from multiprocessing import Process

# 日志文件路径
LOG_FILE = os.path.join(os.path.dirname(os.path.abspath(__file__)), "client_sync_log.txt")
SYNC_RESULTS_LOG = os.path.join(os.path.dirname(os.path.abspath(__file__)), "sync_results_log.txt")

def check_root():
    """检查是否以root身份运行"""
    if os.geteuid() != 0:
        # 如果不是root身份，弹出一个独立的提示窗口
        def show_warning():
            warning_root = tk.Tk()
            warning_root.title("权限警告")
            warning_label = tk.Label(warning_root, text="请以root身份运行此程序！", font=("Arial", 16), fg="red")
            warning_label.pack(pady=20)
            warning_root.mainloop()

        # 使用单独的进程运行提示窗口
        warning_process = Process(target=show_warning)
        warning_process.start()
        return False
    return True

class AutoCloseDialog:
    def __init__(self, parent, title, prompt, timeout=10):
        self.parent = parent
        self.timeout = timeout
        self.result = None

        # 创建对话框
        self.dialog = tk.Toplevel(parent)
        self.dialog.title(title)
        self.dialog.geometry("300x100")

        # 提示文本
        self.label = tk.Label(self.dialog, text=prompt)
        self.label.pack(pady=10)

        # 输入框
        self.entry = tk.Entry(self.dialog)
        self.entry.pack(pady=5)

        # 确定按钮
        self.ok_button = tk.Button(self.dialog, text="确定", command=self.on_ok)
        self.ok_button.pack(pady=5)

        # 启动定时器
        self.timer = threading.Timer(self.timeout, self.on_timeout)
        self.timer.start()

    def on_ok(self):
        """用户点击确定按钮时的处理"""
        self.result = self.entry.get()
        self.dialog.destroy()
        self.timer.cancel()  # 取消定时器

    def on_timeout(self):
        """超时处理"""
        self.result = None
        self.dialog.destroy()

    def show(self):
        """显示对话框并返回结果"""
        self.dialog.wait_window()  # 等待对话框关闭
        return self.result

class ChronyClientApp:
    def __init__(self, root):
        self.root = root
        self.root.title("Chrony校时程序")

        # 线程运行标志
        self.running = True

        # 获取所有网卡
        self.interfaces = self.get_network_interfaces()
        self.external_interface = self.interfaces[0] if self.interfaces else None

        # 时间服务器列表
        self.time_servers = []

        # 当前时间源状态
        self.current_time_source = "未知"

        # 同步间隔（默认值为10分钟）
        self.sync_interval = 10  # 单位：分钟

        # 最后一次同步结果
        self.last_sync_result = "无"

        # 同步线程是否运行
        self.sync_thread_running = True

        # 定时任务
        self.sync_timer = None

        # GUI组件
        self.time_label = tk.Label(root, text="当前时间: ", font=("Arial", 24))
        self.time_label.pack(pady=20)

        self.network_status_label = tk.Label(root, text="网络状态: 未知", font=("Arial", 16))
        self.network_status_label.pack(pady=10)

        self.ip_info_label = tk.Label(root, text="网卡IP信息: 无", font=("Arial", 16))
        self.ip_info_label.pack(pady=10)

        self.sync_status_label = tk.Label(root, text="与时间服务器同步状态: 未知", font=("Arial", 16))
        self.sync_status_label.pack(pady=10)

        self.chrony_status_label = tk.Label(root, text="Chrony服务状态: 未知", font=("Arial", 16))
        self.chrony_status_label.pack(pady=10)

        self.client_sync_label = tk.Label(root, text="客户端校时情况: 无", font=("Arial", 16))
        self.client_sync_label.pack(pady=10)

        self.time_source_label = tk.Label(root, text="当前时间源: 未知", font=("Arial", 16))
        self.time_source_label.pack(pady=10)

        self.last_sync_label = tk.Label(root, text="最后一次同步结果: 无", font=("Arial", 16))
        self.last_sync_label.pack(pady=10)

        # 创建一个Frame来放置按钮
        self.button_frame = tk.Frame(root)
        self.button_frame.pack(pady=10)

        self.start_chrony_button = tk.Button(self.button_frame, text="启动Chrony服务", command=self.start_chrony_service)
        self.start_chrony_button.pack(side=tk.LEFT, padx=5)  # 放在左侧

        self.stop_chrony_button = tk.Button(self.button_frame, text="停止Chrony服务", command=self.stop_chrony_service)
        self.stop_chrony_button.pack(side=tk.LEFT, padx=5)  # 放在左侧

        # 添加设置菜单
        self.menu_bar = tk.Menu(root)
        self.settings_menu = tk.Menu(self.menu_bar, tearoff=0)
        self.settings_menu.add_command(label="添加时间服务器", command=self.add_time_server)
        self.settings_menu.add_command(label="设置同步间隔", command=self.set_sync_interval)
        self.menu_bar.add_cascade(label="设置", menu=self.settings_menu)
        self.root.config(menu=self.menu_bar)

        # 启动时间更新线程
        self.time_thread = threading.Thread(target=self.update_time, daemon=True)
        self.time_thread.start()

        # 启动同步状态检查线程
        self.sync_thread = threading.Thread(target=self.check_sync_status, daemon=True)
        self.sync_thread.start()

        # 启动客户端校时情况检查线程
        self.client_sync_thread = threading.Thread(target=self.check_client_sync, daemon=True)
        self.client_sync_thread.start()

        # 启动互联网连接检测线程
        self.internet_check_thread = threading.Thread(target=self.check_internet_connection, daemon=True)
        self.internet_check_thread.start()

        # 启动IP信息更新线程
        self.ip_info_thread = threading.Thread(target=self.update_ip_info, daemon=True)
        self.ip_info_thread.start()

        # 初始化按钮状态
        self.update_button_state()

        # 绑定窗口关闭事件
        self.root.protocol("WM_DELETE_WINDOW", self.on_close)

    def on_close(self):
        """窗口关闭时的处理"""
        # 设置线程运行标志为 False
        self.running = False

        # 取消同步定时任务
        if self.sync_timer:
            self.sync_timer.cancel()

        # 关闭窗口
        self.root.destroy()

    def get_network_interfaces(self):
        # 获取所有网卡接口，排除lo
        interfaces = []
        try:
            output = subprocess.check_output(["ip", "addr", "show"]).decode("utf-8")
            for line in output.splitlines():
                if "mtu" in line and "lo" not in line:
                    interface = line.split(":")[1].strip()
                    interfaces.append(interface)
        except Exception as e:
            messagebox.showerror("错误", f"获取网卡接口失败: {e}")
        return interfaces

    def update_time(self):
        # 更新当前时间
        while self.running:
            current_time = time.strftime("%Y-%m-%d %H:%M:%S")
            self.time_label.config(text=f"当前时间: {current_time}")
            time.sleep(1)

    def check_sync_status(self):
        # 检查与时间服务器的同步状态
        while self.running and self.sync_thread_running:
            try:
                output = subprocess.check_output(["chronyc", "tracking"], timeout=5).decode("utf-8")
                if "Leap status" in output:
                    self.sync_status_label.config(text="与时间服务器同步状态: 已同步", fg="green")
                    self.last_sync_result = f"同步成功 - {time.strftime('%Y-%m-%d %H:%M:%S')}"
                    self.root.after(0, self.update_last_sync_label, self.last_sync_result, "green")
                    # 将同步结果写入日志文件
                    with open(SYNC_RESULTS_LOG, "a") as log_file:
                        log_file.write(f"{self.last_sync_result}\n")
                else:
                    self.sync_status_label.config(text="与时间服务器同步状态: 未同步", fg="red")
                    self.last_sync_result = f"同步失败 - {time.strftime('%Y-%m-%d %H:%M:%S')}"
                    self.root.after(0, self.update_last_sync_label, self.last_sync_result, "red")
                    # 将同步结果写入日志文件
                    with open(SYNC_RESULTS_LOG, "a") as log_file:
                        log_file.write(f"{self.last_sync_result}\n")
            except subprocess.TimeoutExpired:
                self.sync_status_label.config(text="与时间服务器同步状态: 超时", fg="orange")
                self.last_sync_result = f"同步超时 - {time.strftime('%Y-%m-%d %H:%M:%S')}"
                self.root.after(0, self.update_last_sync_label, self.last_sync_result, "orange")
                # 将同步结果写入日志文件
                with open(SYNC_RESULTS_LOG, "a") as log_file:
                    log_file.write(f"{self.last_sync_result}\n")
            except Exception as e:
                self.sync_status_label.config(text="与时间服务器同步状态: 未知", fg="orange")
                self.last_sync_result = f"同步未知 - {time.strftime('%Y-%m-%d %H:%M:%S')}"
                self.root.after(0, self.update_last_sync_label, self.last_sync_result, "orange")
                # 将同步结果写入日志文件
                with open(SYNC_RESULTS_LOG, "a") as log_file:
                    log_file.write(f"{self.last_sync_result}\n")
            time.sleep(self.sync_interval * 60)  # 根据设置的同步间隔检查

    def check_client_sync(self):
        # 检查客户端校时情况
        while self.running:
            try:
                output = subprocess.check_output(["chronyc", "clients"], timeout=5).decode("utf-8")
                # 提取前50个客户端校时结果
                lines = output.splitlines()
                if len(lines) > 1:  # 第一行是表头
                    clients = lines[1:51]  # 取前50个客户端
                    client_info = "\n".join(clients)
                    self.client_sync_label.config(text=f"客户端校时情况:\n{client_info}")
                    # 将结果写入日志文件，并记录当前时间
                    with open(LOG_FILE, "a") as log_file:
                        log_file.write(f"{time.strftime('%Y-%m-%d %H:%M:%S')} - 客户端校时结果:\n{client_info}\n\n")
                else:
                    self.client_sync_label.config(text="客户端校时情况: 无", fg="orange")
                    # 如果没有客户端，记录无客户端的情况
                    with open(LOG_FILE, "a") as log_file:
                        log_file.write(f"{time.strftime('%Y-%m-%d %H:%M:%S')} - 无客户端校时结果\n\n")
            except subprocess.TimeoutExpired:
                self.client_sync_label.config(text="客户端校时情况: 超时", fg="orange")
                # 记录超时情况
                with open(LOG_FILE, "a") as log_file:
                    log_file.write(f"{time.strftime('%Y-%m-%d %H:%M:%S')} - 客户端校时超时\n\n")
            except Exception as e:
                self.client_sync_label.config(text="客户端校时情况: 未知", fg="orange")
                # 记录未知错误
                with open(LOG_FILE, "a") as log_file:
                    log_file.write(f"{time.strftime('%Y-%m-%d %H:%M:%S')} - 客户端校时未知错误: {e}\n\n")
            time.sleep(10)

    def check_internet_connection(self):
        # 检测互联网连接
        while self.running:
            if self.is_internet_available():
                self.network_status_label.config(text="网络状态: 已连接", fg="green")
                self.current_time_source = "互联网时间服务器"
                self.update_chrony_config()  # 使用互联网时间服务器
            else:
                self.network_status_label.config(text="网络状态: 未连接", fg="red")
                self.current_time_source = "本地时钟"
                self.enable_local_time_source()  # 切换到本地时钟
            self.time_source_label.config(text=f"当前时间源: {self.current_time_source}")
            time.sleep(60)  # 每分钟检测一次

    def is_internet_available(self, host="8.8.8.8", port=53, timeout=3):
        # 检测是否能够连接到互联网
        try:
            socket.setdefaulttimeout(timeout)
            socket.socket(socket.AF_INET, socket.SOCK_STREAM).connect((host, port))
            return True
        except Exception:
            return False

    def update_ip_info(self):
        # 更新网卡IP信息
        while self.running:
            try:
                output = subprocess.check_output(["ip", "addr", "show"], timeout=5).decode("utf-8")
                ip_info = []
                for line in output.splitlines():
                    if "inet " in line and "127.0.0.1" not in line:
                        parts = line.strip().split()
                        interface = parts[-1]
                        ip_address = parts[1].split("/")[0]
                        ip_info.append(f"{interface}: {ip_address}")
                self.ip_info_label.config(text=f"网卡IP信息: {', '.join(ip_info)}")
            except subprocess.TimeoutExpired:
                self.ip_info_label.config(text="网卡IP信息: 超时", fg="orange")
            except Exception as e:
                self.ip_info_label.config(text="网卡IP信息: 获取失败", fg="orange")
            time.sleep(10)  # 每10秒更新一次

    def enable_local_time_source(self):
        # 切换到本地时钟
        try:
            with open("/etc/chrony/chrony.conf", "r") as f:
                existing_config = f.readlines()
            
            with open("/etc/chrony/chrony.conf", "w") as f:
                for line in existing_config:
                    if not line.startswith("server "):
                        f.write(line)
                f.write("server 127.127.1.0\n")  # 使用本地时钟
                f.write("allow 0/0\n")  # 允许所有客户端连接
            subprocess.run(["sudo", "systemctl", "restart", "chrony"], check=True)
        except Exception as e:
            messagebox.showerror("错误", f"切换到本地时钟失败: {e}")

    def start_chrony_service(self):
        # 启动Chrony服务
        try:
            subprocess.run(["sudo", "systemctl", "start", "chrony"], check=True)
            self.chrony_status_label.config(text="Chrony服务状态: 已启动", fg="green")
            self.update_button_state()
        except Exception as e:
            messagebox.showerror("错误", f"启动Chrony服务失败: {e}")

    def stop_chrony_service(self):
        # 停止Chrony服务
        try:
            subprocess.run(["sudo", "systemctl", "stop", "chrony"], check=True)
            self.chrony_status_label.config(text="Chrony服务状态: 已停止", fg="red")
            self.update_button_state()
        except Exception as e:
            messagebox.showerror("错误", f"停止Chrony服务失败: {e}")

    def add_time_server(self):
        # 添加时间服务器
        time_server = simpledialog.askstring("添加时间服务器", "请输入时间服务器地址:")
        if time_server:
            self.time_servers.append(time_server)
            self.update_chrony_config()
            messagebox.showinfo("成功", "时间服务器已添加并Chrony服务已重启")

    def set_sync_interval(self):
        # 设置同步间隔
        dialog = AutoCloseDialog(self.root, "设置同步间隔", "请输入同步间隔（分钟）:", timeout=10)
        interval = dialog.show()

        if interval is not None:
            try:
                interval = int(interval)
                if 1 <= interval <= 1440:
                    self.sync_interval = interval
                    messagebox.showinfo("成功", f"同步间隔已设置为 {interval} 分钟")
                    self.start_sync_task()
                else:
                    messagebox.showerror("错误", "同步间隔必须在1到1440分钟之间")
            except ValueError:
                messagebox.showerror("错误", "请输入有效的数字")
        else:
            messagebox.showinfo("超时", "未输入同步间隔，已取消设置")

    def start_sync_task(self):
        # 如果已有定时任务，先取消
        if self.sync_timer:
            self.sync_timer.cancel()

        # 执行同步操作
        self.sync_time()

        # 设置下一次同步任务
        self.sync_timer = threading.Timer(self.sync_interval * 60, self.start_sync_task)
        self.sync_timer.start()

    def sync_time(self):
        # 执行chrony的makestep命令并获取结果
        try:
            # 使用subprocess.run执行makestep命令
            result = subprocess.run(["chronyc", "makestep"], capture_output=True, text=True, check=True)
            
            # 获取命令的输出
            output = result.stdout
            
            # 获取当前同步的上游服务器信息
            tracking_output = subprocess.check_output(["chronyc", "tracking"], timeout=5).decode("utf-8")
            upstream_server = "未知"
            for line in tracking_output.splitlines():
                if "Reference ID" in line:
                    upstream_server = line.split(":")[1].strip()
                    break
            
            # 更新最后一次同步结果
            self.last_sync_result = f"同步成功 - {time.strftime('%Y-%m-%d %H:%M:%S')}"
            
            # 使用 after 方法在主线程中更新 GUI
            self.root.after(0, self.update_last_sync_label, self.last_sync_result, "green")
            
            # 将同步结果和上游服务器信息写入日志文件
            with open(SYNC_RESULTS_LOG, "a") as log_file:
                log_file.write(f"{time.strftime('%Y-%m-%d %H:%M:%S')} - 同步成功\n")
                log_file.write(f"上游服务器: {upstream_server}\n")
                log_file.write(f"同步结果:\n{output}\n\n")
            
            # 显示makestep的结果（自动关闭弹窗）
            self.show_auto_close_message("makestep结果", f"makestep命令执行结果:\n{output}")
        except subprocess.CalledProcessError as e:
            # 如果命令执行失败，显示错误信息
            self.last_sync_result = f"同步失败 - {time.strftime('%Y-%m-%d %H:%M:%S')}"
            self.root.after(0, self.update_last_sync_label, self.last_sync_result, "red")
            
            # 记录同步失败和错误信息
            with open(SYNC_RESULTS_LOG, "a") as log_file:
                log_file.write(f"{time.strftime('%Y-%m-%d %H:%M:%S')} - 同步失败\n")
                log_file.write(f"错误信息: {e.stderr}\n\n")
            
            self.show_auto_close_message("错误", f"makestep命令执行失败: {e.stderr}")
        except Exception as e:
            # 其他异常情况
            self.last_sync_result = f"同步未知 - {time.strftime('%Y-%m-%d %H:%M:%S')}"
            self.root.after(0, self.update_last_sync_label, self.last_sync_result, "orange")
            
            # 记录同步未知错误
            with open(SYNC_RESULTS_LOG, "a") as log_file:
                log_file.write(f"{time.strftime('%Y-%m-%d %H:%M:%S')} - 同步未知错误\n")
                log_file.write(f"错误信息: {e}\n\n")
            
            self.show_auto_close_message("错误", f"执行makestep命令时发生错误: {e}")

    def update_last_sync_label(self, text, color):
        """更新最后一次同步结果的标签"""
        self.last_sync_label.config(text=f"最后一次同步结果: {text}", fg=color)

    def show_auto_close_message(self, title, message, timeout=5):
        """显示一个自动关闭的弹窗"""
        # 创建弹窗
        popup = tk.Toplevel(self.root)
        popup.title(title)
        popup.geometry("400x200")

        # 显示消息
        label = tk.Label(popup, text=message, font=("Arial", 12))
        label.pack(pady=20)

        # 设置定时器，在 timeout 秒后关闭弹窗
        popup.after(timeout * 1000, popup.destroy)

    def update_chrony_config(self):
        # 更新Chrony配置文件
        try:
            with open("/etc/chrony/chrony.conf", "r") as f:
                existing_config = f.readlines()

            # 检查是否已经存在 allow 0/0
            allow_exists = any("allow 0/0" in line for line in existing_config)

            with open("/etc/chrony/chrony.conf", "w") as f:
                for line in existing_config:
                    if not line.startswith("server "):
                        f.write(line)
                for server in self.time_servers:
                    f.write(f"server {server} minpoll {self.sync_interval} maxpoll {self.sync_interval}\n")
                if not allow_exists:
                    f.write("allow 0/0\n")  # 如果不存在则添加
            subprocess.run(["sudo", "systemctl", "restart", "chrony"], check=True)
        except Exception as e:
            messagebox.showerror("错误", f"更新Chrony配置文件失败: {e}")

    def update_button_state(self):
        # 更新按钮状态
        try:
            # 检查chrony服务状态
            status = subprocess.check_output(["systemctl", "is-active", "chrony"], timeout=5).decode("utf-8").strip()
            if status == "active":
                self.chrony_status_label.config(text="Chrony服务状态: 已启动", fg="green")
                self.start_chrony_button.config(state=tk.DISABLED)
                self.stop_chrony_button.config(state=tk.NORMAL)
            elif status == "inactive":
                self.chrony_status_label.config(text="Chrony服务状态: 已停止", fg="red")
                self.start_chrony_button.config(state=tk.NORMAL)
                self.stop_chrony_button.config(state=tk.DISABLED)
            else:
                self.chrony_status_label.config(text="Chrony服务状态: 未知", fg="orange")
                self.start_chrony_button.config(state=tk.NORMAL)
                self.stop_chrony_button.config(state=tk.DISABLED)
        except subprocess.TimeoutExpired:
            self.chrony_status_label.config(text="Chrony服务状态: 超时", fg="orange")
            self.start_chrony_button.config(state=tk.NORMAL)
            self.stop_chrony_button.config(state=tk.DISABLED)
        except Exception as e:
            # 其他异常情况
            self.chrony_status_label.config(text="Chrony服务状态: 未知", fg="orange")
            self.start_chrony_button.config(state=tk.NORMAL)
            self.stop_chrony_button.config(state=tk.DISABLED)

if __name__ == "__main__":
    # 检查是否以root身份运行
    if not check_root():
        sys.exit(1)  # 如果不是root身份，退出程序

    root = tk.Tk()
    app = ChronyClientApp(root)
    root.mainloop()
